#ifndef DISK_H_
#define DISK_H_

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <pthread.h>


using namespace std;

typedef unsigned char byte;

#define POFILE 0
#define DIRECTORY 1
#define SOFTLINK 2
//
//#define max(a,b) ((a) > (b) ? (a) : (b))
//#define min(a,b) ((a) > (b) ? (b) : (a))

typedef struct {
	//General Data:
	int num_blocks; //The size of the file system (in blocks)
	int block_size; //The size of a block (in bytes, minimum size is 64 bytes).
	int root_inode; //Number of root i-node.

	//Blocks Control:
	int num_free_blocks;
	int index_of_first_free_block;

	//Inode Control
	int size_inodes_array;
	int num_free_inodes;
	int index_of_first_free_inode;
} SuperBlock;

typedef struct {
	int file_type;
	int num_hard_links;
	int file_size;
	int direct_block[10];
	int indirect_block;
} Inode;

class Disk
{
private:
	byte* disk;
	SuperBlock* superblock;
	int* free_blocks; //head node in the free blocks list
	int* free_inodes; //head node in the free inodes list
	Inode* inodes_array;

	size_t blockSize;
	int numBlocks;
	//fstream disk;

	pthread_mutex_t	m_blocksMutex;
	pthread_mutex_t	m_inodesMutex;

	void initMembers();

public:
	Disk();
	Disk(int blockSizeInBytes, int numberOfInodes, int diskSizeInBytes);
	virtual ~Disk();
	void dumpToFile();
	void loadFromFile();

	int allocateInode();// – allocates a new i-node and returns its number
	int freeInode(int inode); //–frees the specified i-node and also frees all the data blocks pointed to by it (directly or indirectly).
	int allocateDataBlock();// – allocates a data block and returns its number
	int freeBlock(int block);// – frees the specified block.
	//Use (and update) the maps in the super block in these allocate/free methods.
	int getInodeType(int inode);// – return the type of the file (normal/directory/soft-link).
	int setInodeType(int inode, int filetype);// – set the type of the file (normal/directory/soft-link).
	int getDataBlock (int inode, int i);// – return the number of the i-th data block containing the data of this file (for 0<=i<=9 this is just stored in the 10 pointers in this i-node, for i>9, you must retrieve it from the indirect data block..).
	int setDataBlock (int inode, int i, int block );// – sets the i-th data block of this file to be dblockNum.
	int readBlock(int block, byte* buf);// – read the entire content of the dblockNum's and copy it into buf.
	int readBlock(int block, byte* buf, int offset, size_t n);
	int writeBlock(int block, const byte* newdata);// – overwrite the content of block with number dblockNum with the newdata block. Assume newdata's length is equal to dataBlockSize.
	int writeBlock(int block, const byte* newdata, int offset, size_t n);
	int getFileSize(int inode);// – return the file size saved in the given i-node. This is the exact size of the file in bytes, not the number of blocks used by this file.
	int setFileSize(int inode, int newSize);// – set the file size.
	//Regular files simply store the data written to them by user programs.
	//A directory file stores a sequence of directory entries, each of which having the
	//following structure:
	//i-node number [4 bytes].
	//file name [12 bytes].

	//Note – file names in dir-entries are kept without their path (so the file name of /ab/cd.out is just 'cd.out' ).
	//Note – all services mentioned above should return appropriate error codes to stderr, when things go bad (for example: requesting to free an i-node that was not allocated, requesting to allocate an i-node when there are none free, asking for the file name of a free i-node, asking the 34th data block of a file that contains only 30 data blocks, etc.).

	int numFreeBlocks();
	int numFreeInodes();
	int getBlockSize();
	int decNumHardLinks(int inode);
	int incNumHardLinks(int inode);
	int getNumHardLinks(int inode);
	int status();
};

#endif /*DISK_H_*/
